home *** CD-ROM | disk | FTP | other *** search
/ Exame Informatica 139 / Exame Informatica 139.iso / Revista / Flash / Uniform Server / diskw / usr / lib / File / Path.pm < prev    next >
Encoding:
Perl POD Document  |  2005-10-11  |  7.8 KB  |  310 lines

  1. package File::Path;
  2.  
  3. =head1 NAME
  4.  
  5. File::Path - create or remove directory trees
  6.  
  7. =head1 SYNOPSIS
  8.  
  9.     use File::Path;
  10.  
  11.     mkpath(['/foo/bar/baz', 'blurfl/quux'], 1, 0711);
  12.     rmtree(['foo/bar/baz', 'blurfl/quux'], 1, 1);
  13.  
  14. =head1 DESCRIPTION
  15.  
  16. The C<mkpath> function provides a convenient way to create directories, even
  17. if your C<mkdir> kernel call won't create more than one level of directory at
  18. a time.  C<mkpath> takes three arguments:
  19.  
  20. =over 4
  21.  
  22. =item *
  23.  
  24. the name of the path to create, or a reference
  25. to a list of paths to create,
  26.  
  27. =item *
  28.  
  29. a boolean value, which if TRUE will cause C<mkpath>
  30. to print the name of each directory as it is created
  31. (defaults to FALSE), and
  32.  
  33. =item *
  34.  
  35. the numeric mode to use when creating the directories
  36. (defaults to 0777), to be modified by the current umask.
  37.  
  38. =back
  39.  
  40. It returns a list of all directories (including intermediates, determined
  41. using the Unix '/' separator) created.
  42.  
  43. If a system error prevents a directory from being created, then the
  44. C<mkpath> function throws a fatal error with C<Carp::croak>. This error
  45. can be trapped with an C<eval> block:
  46.  
  47.   eval { mkpath($dir) };
  48.   if ($@) {
  49.     print "Couldn't create $dir: $@";
  50.   }
  51.  
  52. Similarly, the C<rmtree> function provides a convenient way to delete a
  53. subtree from the directory structure, much like the Unix command C<rm -r>.
  54. C<rmtree> takes three arguments:
  55.  
  56. =over 4
  57.  
  58. =item *
  59.  
  60. the root of the subtree to delete, or a reference to
  61. a list of roots.  All of the files and directories
  62. below each root, as well as the roots themselves,
  63. will be deleted.
  64.  
  65. =item *
  66.  
  67. a boolean value, which if TRUE will cause C<rmtree> to
  68. print a message each time it examines a file, giving the
  69. name of the file, and indicating whether it's using C<rmdir>
  70. or C<unlink> to remove it, or that it's skipping it.
  71. (defaults to FALSE)
  72.  
  73. =item *
  74.  
  75. a boolean value, which if FALSE (the default for non-root users) will
  76. cause C<rmtree> to adjust the mode of directories (if required) prior
  77. to attempting to remove the contents.  Note that on interruption or
  78. failure of C<rmtree>, directories may be left with more permissive
  79. modes for the owner.
  80.  
  81. =back
  82.  
  83. It returns the number of files successfully deleted.  Symlinks are
  84. simply deleted and not followed.
  85.  
  86. =head1 DIAGNOSTICS
  87.  
  88. =over 4
  89.  
  90. =item *
  91.  
  92. On Windows, if C<mkpath> gives you the warning: B<No such file or
  93. directory>, this may mean that you've exceeded your filesystem's
  94. maximum path length.
  95.  
  96. =back
  97.  
  98. =head1 AUTHORS
  99.  
  100. Tim Bunce <F<Tim.Bunce@ig.co.uk>> and
  101. Charles Bailey <F<bailey@newman.upenn.edu>>
  102.  
  103. =cut
  104.  
  105. use 5.006;
  106. use Carp;
  107. use File::Basename ();
  108. use Exporter ();
  109. use strict;
  110. use warnings;
  111. use Cwd 'getcwd';
  112.  
  113. our $VERSION = "1.07";  # but modified for ActivePerl
  114. our @ISA = qw( Exporter );
  115. our @EXPORT = qw( mkpath rmtree );
  116.  
  117. my $Is_VMS = $^O eq 'VMS';
  118. my $Is_MacOS = $^O eq 'MacOS';
  119.  
  120. # These OSes complain if you want to remove a file that you have no
  121. # write permission to:
  122. my $force_writeable = ($^O eq 'os2' || $^O eq 'dos' || $^O eq 'MSWin32' ||
  123.                $^O eq 'amigaos' || $^O eq 'MacOS' || $^O eq 'epoc');
  124.  
  125. sub mkpath {
  126.     my($paths, $verbose, $mode) = @_;
  127.     # $paths   -- either a path string or ref to list of paths
  128.     # $verbose -- optional print "mkdir $path" for each directory created
  129.     # $mode    -- optional permissions, defaults to 0777
  130.     local($")=$Is_MacOS ? ":" : "/";
  131.     $mode = 0777 unless defined($mode);
  132.     $paths = [$paths] unless ref $paths;
  133.     my(@created,$path);
  134.     foreach $path (@$paths) {
  135.     $path .= '/' if $^O eq 'os2' and $path =~ /^\w:\z/s; # feature of CRT 
  136.     # Logic wants Unix paths, so go with the flow.
  137.     if ($Is_VMS) {
  138.         next if $path eq '/';
  139.         $path = VMS::Filespec::unixify($path);
  140.         if ($path =~ m:^(/[^/]+)/?\z:) {
  141.             $path = $1.'/000000';
  142.         }
  143.     }
  144.     next if -d $path;
  145.     my $parent = File::Basename::dirname($path);
  146.     unless (-d $parent or $path eq $parent) {
  147.         push(@created,mkpath($parent, $verbose, $mode));
  148.      }
  149.     print "mkdir $path\n" if $verbose;
  150.     unless (mkdir($path,$mode)) {
  151.         my $e = $!;
  152.         # allow for another process to have created it meanwhile
  153.         $! = $e, croak "mkdir $path: $e" unless -d $path;
  154.     }
  155.     push(@created, $path);
  156.     }
  157.     @created;
  158. }
  159.  
  160. sub _rmtree
  161. {
  162.     my ($path, $prefix, $up, $up_dev, $up_ino, $verbose, $safe) = @_;
  163.  
  164.     my ($dev, $ino, $perm) = lstat $path or do {
  165.     return 0;
  166.     };
  167.     $perm &= 07777;
  168.  
  169.     unless (-d _)
  170.     {
  171.     my $nperm;
  172.     if ($force_writeable) {
  173.         # make the file writable
  174.         $nperm = $perm | 0600;
  175.         unless ($safe or $nperm == $perm or chmod $nperm, $path) {
  176.         carp "Can't make file $prefix$path writeable: $!";
  177.         }
  178.     }
  179.     print "unlink $prefix$path\n" if $verbose;
  180.     unless (unlink $path)
  181.     {
  182.         carp "Can't remove file $prefix$path ($!)";
  183.         if ($force_writeable) {
  184.         unless ($safe or $nperm == $perm or chmod $perm, $path) {
  185.             carp("and can't restore permissions to "
  186.              . sprintf("0%o",$perm) . "\n");
  187.         }
  188.         }
  189.         return 0;
  190.     }
  191.     return 1;
  192.     }
  193.  
  194.     CHDIR: {
  195.     last CHDIR if chdir $path;
  196.     my $err = $!;
  197.     unless ($safe || ($perm & 0100)) { 
  198.         # might be able to succeed by tweaking the permission
  199.         # before we chdir
  200.         last CHDIR if chmod(0700, $path) && chdir($path);
  201.     }
  202.     carp "Can't chdir to $prefix$path ($err)";
  203.     return 0;
  204.     }
  205.  
  206.     # avoid a race condition where a directory may be replaced by a
  207.     # symlink between the initial lstat and the chdir
  208.     my ($new_dev, $new_ino) = stat '.';
  209.     unless ("$new_dev:$new_ino" eq "$dev:$ino")
  210.     {
  211.     croak "Directory $prefix$path changed before chdir, aborting";
  212.     }
  213.  
  214.     my $nperm = $perm | 0700;
  215.     unless ($safe or $nperm == $perm or chmod $nperm, '.')
  216.     {
  217.     carp "Can't make directory $prefix$path read+writeable ($!)";
  218.     $nperm = $perm;
  219.     }
  220.  
  221.     my $count = 0;
  222.     if (opendir my $dir, '.')
  223.     {
  224.     my $entry;
  225.     while (defined ($entry = readdir $dir))
  226.     {
  227.         next if $entry =~ /^\.\.?$/;
  228.         $entry =~ /^(.*)$/s; $entry = $1; # untaint
  229.         $count += _rmtree($entry, "$prefix$path/", '..', $dev, $ino,
  230.         $verbose, $safe);
  231.     }
  232.  
  233.     closedir $dir;
  234.     }
  235.  
  236.     # restore directory permissions is required (in case the rmdir
  237.     # below fails) now, while we're still in the directory and may do
  238.     # so without a race via '.'
  239.     unless ($force_writeable or $safe or $nperm == $perm or chmod $perm, '.')
  240.     {
  241.     carp "Can't restore permissions on directory $prefix$path ($!)";
  242.     }
  243.  
  244.     # don't leave the caller in an unexpected directory
  245.     unless (chdir $up)
  246.     {
  247.     croak "Can't return to $up from $prefix$path ($!)";
  248.     }
  249.  
  250.     # ensure that a chdir ..  didn't take us somewhere other than
  251.     # where we expected (see CVE-2002-0435)
  252.     unless (($new_dev, $new_ino) = stat '.'
  253.     and "$new_dev:$new_ino" eq "$up_dev:$up_ino")
  254.     {
  255.     croak "Previous directory $up changed since entering $prefix$path";
  256.     }
  257.  
  258.     print "rmdir $prefix$path\n" if $verbose;
  259.     if (rmdir $path)
  260.     {
  261.     $count++;
  262.     }
  263.     else
  264.     {
  265.     carp "Can't remove directory $prefix$path ($!)";
  266.     }
  267.  
  268.     return $count;
  269. }
  270.  
  271. sub rmtree
  272. {
  273.     my ($p, $verbose, $safe) = @_;
  274.     $p = [] unless defined $p and length $p;
  275.     $p = [ $p ] unless ref $p;
  276.     my @paths = grep defined && length, @$p;
  277.  
  278.     # default to "unsafe" for non-root (will chmod dirs)
  279.     $safe = ($> || $force_writeable) ? 0 : 1 unless defined $safe;
  280.  
  281.     unless (@paths)
  282.     {
  283.     carp "No root path(s) specified";
  284.     return 0;
  285.     }
  286.  
  287.     my $oldpwd = getcwd or do {
  288.     carp "Can't fetch initial working directory";
  289.     return 0;
  290.     };
  291.  
  292.     my ($dev, $ino) = stat '.' or do {
  293.     carp "Can't stat initial working directory";
  294.     return 0;
  295.     };
  296.  
  297.     # untaint
  298.     for ($oldpwd) { /^(.*)$/s; $_ = $1 }
  299.  
  300.     my $count = 0;
  301.     for my $path (@paths)
  302.     {
  303.     $count += _rmtree($path, '', $oldpwd, $dev, $ino, $verbose, $safe);
  304.     }
  305.  
  306.     $count;
  307. }
  308.  
  309. 1;
  310.